###############################################################################
##
## acnet.R
## Functions and data structures for working with Amicus Curiae Networks data
## available at http://amicinetworks.com.
##
## Dino Christenson
## http://people.bu.edu/dinopc/
##
## Boston University Software & Application Innovation Lab
## http://sail.bu.edu
##

###############################################################################
## Dependencies.

library(jsonlite)
library(igraph) # Has namespace, so it is loaded first.
library(network) # Load this after igraph to avoid namespace collisions.
library(statnet)
library(intergraph)

###############################################################################
## Functions.

#' Return location of online data resource as a string.
#' 
#' @param start Index of first result from which the returned data should begin.
#' @param length Number of results to return (starting at start).
#' @param minRange Lower bound on year range.
#' @param maxRange Upper bound on year range.
#' @param search Search parameter.
#'
#' @export
acnet.web.query <- function (start=0, length=99999, minRange=2010, maxRange=2012, search="") {
  domain = "http://amicinetworks.com/"
  service = "api.php?"
  #data[caseid]=&data[and]=1&data[organizations]=&data[pageNumber]=0&data[pageLength]=50

  # Build up the parameter list for the request.
  start = paste(c("start=", start), collapse="")
  length = paste(c("length=", length), collapse="")
  minRange = paste(c("data[minRange]=", minRange), collapse="")
  maxRange = paste(c("data[maxRange]=", maxRange), collapse="")
  search = paste(c("search[value]=", search), collapse="")
  parameters = paste(c(start, length, minRange, maxRange, search), collapse="&")

  # Return the request string.
  return (paste(c(domain, service, parameters), collapse=""))
}

#' Build network from data in JSON file by using statnet.
#' 
#' @param filepath A path to a file (as a string).
#' @param multigraph Plot multiple edges (will use edge thickness, otherwise).
#' @param opinionYear List of years for which to include data.
#' @param type Specifies whether output is "network" or "igraph" object.
#'
#' @export
acnet.fromJSON <- function (filepath, multigraph=TRUE, opinionYear=NULL, type="network") {
  data <- jsonlite::fromJSON(readLines(filepath, warn=F))
  briefs_with_signers = length(data$data$organizationNames)
  organizations = c()

  # Collect the list of all organizations in the brief data.
  if (briefs_with_signers > 0) {
    for (i in 1:briefs_with_signers) {
      if (is.null(opinionYear) || (data$data$opinionYear[[i]] %in% opinionYear)) {
        orgs = strsplit(data$data$organizationNames[[i]], "[|]")[[1]]
        orgs = orgs[orgs!=""]
        organizations = union(organizations, orgs)
      }
    }
  }

  net <- network::network.initialize(
    length(organizations), 
    loops=FALSE,
    multiple=multigraph,
    directed=FALSE)
  if (length(organizations) == 0)
    return(net)
  network::network.vertex.names(net) <- organizations

  # Add edge between every pair of organizations that signed the
  # same brief.
  for (i in 1:briefs_with_signers) {
    if (is.null(opinionYear) || data$data$opinionYear[[i]] %in% opinionYear) {
      orgs = strsplit(data$data$organizationNames[[i]], "[|]")[[1]]
      signers = orgs[orgs!=""]
      for (j in 1:length(signers)) {
        for (k in 1:length(signers)) {
          if (j > k) { # No self-loops or bidirectional edges.
            source = match(signers[j], organizations)
            target = match(signers[k], organizations)
            briefAttrs = c("briefID", "caseID", "name", "direction", "opinionYear")
            briefVals = c(data$data$briefID[[i]], data$data$caseID[[i]], data$data$name[[i]], data$data$direction[[i]], data$data$opinionYear[[i]])
            if (length(network::get.edges(net, source, target)) < 1) {
              if (multigraph == TRUE) {
                network::add.edge(net, source, target, c("curve", briefAttrs), c(0.03, briefVals))
              } else {
                network::add.edge(net, source, target, c("count", "width", "curve"), c(1, 0.5, 0))
              }
            } else {
              if (multigraph == TRUE) {
                existing = length(network::get.edgeIDs(net, source, target))
                network::add.edge(net, source, target, c("curve", briefAttrs), c(0.03*(existing+1), briefVals))
              } else {
                edge = network::get.edgeIDs(net, source, target)
                count = network::get.edge.attribute(net, "count")[edge]
                width = network::get.edge.attribute(net, "width")[edge]
                curve = network::get.edge.attribute(net, "curve")[edge]
                network::set.edge.attribute(net, c("count", "width", "curve"), c(count+1, width*2, 0), edge)
              }
            }
          } # avoid symmetric duplicates
        } # for signers in brief
      } # for signers in brief
    } # if filter apply
    print(i/length(briefs_with_signers))
  } # for all briefs

  if (type == "igraph") {
    net = intergraph::asIgraph(net)
  } else if (type == "network") {
    # Nothing to do, as "network" is the default.
  } else {
    stop("The type parameter must either be \"network\" or \"igraph\".")
  }

  return (net)
}

## eof